﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using epk23.PartIIProject.Exceptions;

namespace epk23.PartIIProject
{
    class Machine
    {
        public List<Rule> rules { get; set; }
        public List<Register> registers { get; set; }

        public int startRuleNumber { get; set; }

        public string description { get; set; }

        public static int intCounter = 1;
        public int myCounter { get; set; }

        public Machine()
        {
            myCounter = intCounter;
            intCounter++;
            registers = new List<Register>();
            rules = new List<Rule>();
            startRuleNumber = 0;
        }
        public void addRule(Rule r)
        {
            rules.Add(r);
        }

        public void addRegister(String regName)
        {
            bool exists = false;
            foreach (Register r in registers)
            {
                if (r.regName.Equals(regName)) 
                { 
                    exists = true;
                    break;
                }
            }
            if (!exists)
            {
                registers.Add(new Register(regName, 0));
            }
        }

        public Register lookupRegister(String regName)
        {
            Register targetRegister = null;
            foreach (Register r in registers)
            {
                if (r.regName == regName)
                {
                    targetRegister = r;
                    break;
                }
            }
            if (targetRegister == null) { throw new RegisterNotFoundException(); }
            return targetRegister;
        }

        public Rule lookupRule(int ruleNumber)
        {
            Rule targetRule = null;
            foreach (Rule r in rules)
            {
                if (r.ruleNumber == ruleNumber) { 
                    targetRule = r;
                    break;
                }
            }
            if (targetRule == null) { throw new RuleNotFoundException(); }
            return targetRule;
        }

        public void removeRule(Rule rule)
        {
            rules.Remove(rule);
            
        }

        public List<String> getRegisters() //gets all register names from machine and all nested machines
        {
            List<String> regNames = new List<String>();
            Machine currentMachine;

            foreach (Rule rule in rules)
            {
                if (rule.targetRegister != null)
                    regNames.Add(rule.targetRegister);

                if (rule.GetType() == typeof(MachineRule))
                {
                    currentMachine = FileManager.readFile(((MachineRule)rule).machineName).Machine;
                    List<String> sublist = currentMachine.getRegisters();
                    foreach (string s in sublist)
                    {
                        regNames.Add(s);
                    }
                }
            }
            return regNames;
        }

        public static int registerComparison(Register r1, Register r2)
        {
            String n1 = r1.regName;
            String n2 = r2.regName;

            return String.Compare(n1, n2);
        }

        public void printRules()
        {
            foreach (Rule r in this.rules)
            {
                if (r.GetType() == typeof(HaltRule))
                {
                    Console.WriteLine(r.ruleNumber + " Halt Rule");
                }
                else if (r.GetType() == typeof(IncrementRule))
                {
                    Console.WriteLine(r.ruleNumber + " Increment Rule: increment register " + r.targetRegister + ", branch to rule " + ((IncrementRule)r).targetRuleNumber);
                }
                else if (r.GetType() == typeof(DecrementRule))
                {
                    Console.WriteLine(r.ruleNumber + " Decrement Rule: decrement register " + r.targetRegister + ", branch to rule " + ((DecrementRule)r).targetRuleNumber + " if > 0, rule " + ((DecrementRule)r).targetRuleNumber2 + " if = 0");
                }
                else if (r.GetType() == typeof(MachineRule))
                {
                    Console.WriteLine(r.ruleNumber + " Machine: "+((MachineRule)r).machineName);
                }
                else throw new InvalidRuleTypeException();

            }
        }

        public void printRegisters()
        {
            foreach (Register r in this.registers)
            {
                Console.WriteLine("Register " + r.regName + ": Value = " + r.value);
            }
        }
    }
}
